package com.slorgs.business.utils

import com.slorgs.business.common.Constant
import com.slorgs.infrastructure.dto.ResponseDTO
import com.slorgs.infrastructure.dto.UserDTO
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64
import freemarker.template.Configuration
import org.slf4j.LoggerFactory
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import java.awt.Color
import java.awt.Font
import java.awt.Graphics
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.annotation.Resource
import javax.imageio.ImageIO

@Component
class ValidateCodeUtils {

    @Resource
    var fmConfiguration: Configuration? = null

    @Resource
    var emailUtil: EmailUtil? = null
    private val randString = "023456789ABCDEFGHJKLMNOPQRSTUVWXYZ" //随机产生数字与字母组合的字符串

    private val width = 95 // 图片宽

    private val height = 25 // 图片高

    private val random = Random()
    private var base64: String? = null

    companion object {
        private val log = LoggerFactory.getLogger(ValidateCodeUtils::class.java)
    }


    /*
     * 获得字体
     */
    private fun getFont(): Font {
        return Font("Fixedsys", Font.BOLD, 18)
    }

    /*
     * 获得颜色
     */
    private fun getRandColor(fc: Int, bc: Int): Color? {
        var fc = fc
        var bc = bc
        if (fc > 255) fc = 255
        if (bc > 255) bc = 255
        val r = fc + random.nextInt(bc - fc - 16)
        val g = fc + random.nextInt(bc - fc - 14)
        val b = fc + random.nextInt(bc - fc - 18)
        return Color(r, g, b)
    }

    /**
     * 生成随机图片
     */
    @Throws(IOException::class)
    fun getRandomCode(stringRedisTemplate: StringRedisTemplate, browserIdentity: String): ResponseDTO? {
        val jsonRespDTO = ResponseDTO()
        // BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
        val image = BufferedImage(width, height, BufferedImage.TYPE_INT_BGR)
        // 产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
        val g = image.graphics
        val stream = ByteArrayOutputStream()
        try {
            g.fillRect(0, 0, width, height) //图片大小
            g.font = Font("Times New Roman", Font.PLAIN, 19) //字体大小
            g.color = getRandColor(110, 133) //字体颜色
            // 绘制干扰线
            // 干扰线数量
            val lineSize = 60
            for (i in 0..lineSize) {
                drawLine(g)
            }
            // 绘制随机字符
            var randomString = ""
            // 随机产生字符数量
            val stringNum = 4
            for (i in 1..stringNum) {
                randomString = drawString(g, randomString, i)
            }
            stringRedisTemplate.opsForValue()[Constant.RANDOM_CODE_KEY + browserIdentity, randomString, 2] =
                TimeUnit.MINUTES
            g.dispose()
            ImageIO.write(image, "JPEG", stream)
            base64 = Base64.encode(stream.toByteArray())
            jsonRespDTO.data = "data:image/jpeg;base64,$base64"
            stream.flush()
            stream.close()
        } catch (e: Exception) {
            log.error("将内存中的图片通过流动形式输出到客户端失败>>>> ", e)
            jsonRespDTO.code = HttpStatus.BAD_REQUEST.value()
            jsonRespDTO.message = "获取验证码异常"
        }
        return jsonRespDTO
    }

    /**
     * 绘制字符串
     */
    private fun drawString(g: Graphics, randomString: String, i: Int): String {
        var randomString = randomString
        g.font = getFont()
        g.color = Color(random.nextInt(101), random.nextInt(111), random.nextInt(121))
        val rand = getRandomString(random.nextInt(randString.length))
        randomString += rand
        g.translate(random.nextInt(3), random.nextInt(3))
        g.drawString(rand, 13 * i, 16)
        return randomString
    }

    /**
     * 绘制干扰线
     */
    private fun drawLine(g: Graphics) {
        val x = random.nextInt(width)
        val y = random.nextInt(height)
        val xl = random.nextInt(13)
        val yl = random.nextInt(15)
        g.drawLine(x, y, x + xl, y + yl)
    }

    /*
     * 获取随机的字符
     */
    fun getRandomString(num: Int): String {
        return randString[num].toString()
    }

    //校验验证码
    fun checkVerify(verifyInput: String?, browserIdentity: String, stringRedisTemplate: StringRedisTemplate): Boolean {
        return try {
            //从redis中获取随机数
            val random = stringRedisTemplate.opsForValue()[Constant.RANDOM_CODE_KEY + browserIdentity]
            if (random != null && "" != random && random.equals(verifyInput, ignoreCase = true)) {
                stringRedisTemplate.delete(Constant.RANDOM_CODE_KEY + browserIdentity)
                return true
            }
            false
        } catch (e: Exception) {
            log.error("验证码校验失败", e)
            false
        }
    }

    fun checkMailCode(userDTO: UserDTO, stringRedisTemplate: StringRedisTemplate): Boolean {
        return try {
            val code = stringRedisTemplate.opsForValue()[Constant.MAIL_PREFIX + userDTO.mail]
            if (code != null && userDTO.code != null) {
                if (code == userDTO.code) {
                    stringRedisTemplate.delete(Constant.MAIL_PREFIX + userDTO.mail)
                    return true
                }
            }
            false
        } catch (e: Exception) {
            log.error("验证码校验失败", e)
            false
        }
    }

    fun getMailCode(mail: String, stringRedisTemplate: StringRedisTemplate): ResponseDTO? {
        val responseDTO = ResponseDTO()
        try {
            val random1 = Random()
            val initCode = "0123456789"
            val chars = CharArray(6)
            for (i in 0..5) {
                chars[i] = initCode[random1.nextInt(10)]
            }
            val code = String(chars)
            stringRedisTemplate.opsForValue()[Constant.MAIL_PREFIX + mail, code, 5] = TimeUnit.MINUTES
            val map: MutableMap<String, String> = HashMap()
            map["name"] = mail
            map["code"] = code
            map["time"] = SimpleDateFormat().format(Date())
            val template = fmConfiguration!!.getTemplate("login_template.html")
            val stringWriter = StringWriter()
            template.process(map, stringWriter)
            emailUtil!!.sendMail(mail, "你正在登入【深蓝数聚】", stringWriter.toString(), true)
            responseDTO.code = HttpStatus.OK.value()
        } catch (e: Exception) {
            log.error("获取验证码失败", e)
            responseDTO.code = HttpStatus.BAD_REQUEST.value()
            responseDTO.message = "获取验证码失败"
        }
        return responseDTO
    }

    fun getWebSocketCode(mail: String, stringRedisTemplate: StringRedisTemplate): ResponseDTO? {
        val responseDTO = ResponseDTO()
        try {
            val random1 = Random()
            val initCode = "0123456789abcdefghijklmnopqrstuvwxyz"
            val chars = CharArray(6)
            for (i in 0..5) {
                chars[i] = initCode[random1.nextInt(36)]
            }
            val code = String(chars)
            stringRedisTemplate.opsForValue()[Constant.AUTH_MAIL_PREFIX + mail, code, 30] = TimeUnit.MINUTES
            emailUtil!!.sendMail(
                mail, "你正在登入【深蓝数聚】",
                "你的验证码是[$code]五分钟内有效，请妥善保管！不要泄露给他人。", false
            )
            responseDTO.setCode(HttpStatus.OK.value())
        } catch (e: Exception) {
            log.error("获取验证码失败", e)
            responseDTO.code = HttpStatus.BAD_REQUEST.value()
            responseDTO.message = "获取验证码失败"
        }
        return responseDTO
    }
}